package bll

import (
	"context"
	"encoding/json"
	"fmt"
	"manager/auth"
	"manager/client/grpc"
	log "manager/collector/logger"
	"manager/config"
	"manager/errors"
	"manager/lib/infra"
	"manager/lib/trace_log"
	"manager/model/po"
	"manager/store/postgres"
	"manager/utils"
	"proto/dto"
	"time"

	"manager/event"
	"manager/model"
	"manager/model/entity"
	"manager/store"
)

type setting struct {
	iSetting store.ISetting
}

var Setting = &setting{
	iSetting: postgres.Setting,
}

func (a *setting) init() func() {
	return func() {}
}

func (a *setting) onEvent(data *event.Data) {
	if data == nil || (data.Type != event.Event_Bind_Bike && data.Type != event.Event_UnBind_Bike && data.Type != event.Event_Bike_Cmd) {
		return
	}

	log.Info("---------------- userBikeSetting onEvent data:", utils.ToJsonString(data))
	switch data.Type {
	case event.Event_Bind_Bike:
		a.createSettings(data.Data)
	case event.Event_UnBind_Bike:
		// 解析参数
		obj := data.Data.(*event.UnBindBike)
		if obj == nil {
			log.Error("setting onEvent err")
			return
		}

		// 删除配置
		a.Delete(context.Background(), &model.SettingDeleteRequest{EbikeId: obj.BikeId})
	case event.Event_Bike_Cmd:
		// 解析参数
		obj := data.Data.(*event.BikeCmd)
		if obj == nil {
			log.Error("setting onEvent err")
			return
		}

		// 参数校验
		if obj.EbikeId == 0 || obj.CmdType == "" {
			log.Errorf("setting onEvent Create default setting params error userId:%v CmdType:%v", obj.EbikeId, obj.CmdType)
			return
		}

		a.updateSettings(obj.EbikeId, obj.CmdType, obj.Body)
	}

}

// updateSettings 更新设置
func (a *setting) updateSettings(bikeId int64, cmd string, data interface{}) {
	switch cmd {
	case "cmd":
		obj := data.(*dto.CommandRequest)
		if obj == nil {
			return
		}

	case "toggle":
		obj := data.(*dto.SwitchRequest)
		if obj == nil {
			return
		}

		update := &model.SettingUpdateRequest{
			EbikeId:         &bikeId,
			EbikeSet:        &model.EbikeSet{},
			HillAssist:      nil,
			HillAssistRange: nil,
			FlyCar:          nil,
			MsgSet:          &model.MsgSet{},
			Type:            nil,
		}

		open := 1
		zero := 0
		// 上坡辅助
		if obj.HillAssist != nil {
			hillAssist := int(*obj.HillAssist)
			hillAssistRange := int(*obj.HillAssist)
			if hillAssist == 0 {
				update.HillAssist = &zero
			}

			if hillAssist > 0 {
				update.HillAssist = &open
				update.HillAssistRange = &hillAssistRange
			}
		}

		// 无感开锁
		if obj.SenselessUnlock != nil {
			unlockValue := int(*obj.SenselessUnlock)
			if unlockValue == 0 {
				update.EbikeSet.OpenLock = &zero
			}

			if unlockValue > 0 {
				update.EbikeSet.OpenLock = &open
				update.EbikeSet.OpenLockDistance = &unlockValue
			}
		}

		// 防飞车
		if obj.PreventOutOfControl != nil {
			value := int(*obj.PreventOutOfControl)
			update.FlyCar = &value
		}

		// 报警器震动灵敏度（震动），等级1~3，值越大越灵敏。
		if obj.VibrationSensitivity != nil {
			value := int(*obj.VibrationSensitivity)
			update.EbikeSet.VibrationSensitivity = &value
		}

		a.Update(context.Background(), update)
	case "set":
		obj := data.(*dto.SettingRequest)
		if obj == nil {
			return
		}

		update := &model.SettingUpdateRequest{
			EbikeId:         &bikeId,
			EbikeSet:        &model.EbikeSet{},
			HillAssist:      nil,
			HillAssistRange: nil,
			FlyCar:          nil,
			MsgSet:          &model.MsgSet{},
			Type:            nil,
		}

		open := 1
		zero := 0

		// 自动锁车
		if obj.AutoLockTime != nil {
			value := int(*obj.AutoLockTime)
			if value == 0 {
				update.EbikeSet.LeaveLock = &zero
			}

			if value > 0 {
				update.EbikeSet.LeaveLock = &open
				update.EbikeSet.LeaveCountdown = &value
			}
		}

		a.Update(context.Background(), update)
	}
}

// createSettings 创建设置
func (a *setting) createSettings(data interface{}) error {
	// 解析参数
	obj := data.(*event.BindBike)
	if obj == nil {
		log.Error("setting onEvent err")
		return nil
	}

	// 参数校验
	if obj.BikeId == 0 || obj.UserId == 0 {
		log.Errorf("setting onEvent Create default setting params error userId:%v bikeId:%v", obj.UserId, obj.BikeId)
		return nil
	}

	// 创建车辆默认配置
	if err := a.Create(context.Background(), &model.SettingCreateRequest{
		UserId:  obj.UserId,
		EbikeId: obj.BikeId,
	}); err != nil {
		log.Errorf("setting onEvent Create default setting error userId:%v bikeId:%v", obj.UserId, obj.BikeId)
		return nil
	}
	return nil
}

func (a *setting) Create(ctx context.Context, in *model.SettingCreateRequest) error {
	var (
		err error
	)

	c := buildSetting(in.UserId, in)
	_, err = a.iSetting.Create(c)
	return err
}

func (a *setting) Update(ctx context.Context, in *model.SettingUpdateRequest) error {
	var (
		dict = make(map[string]interface{})
	)

	// todo:校验车辆、校验用户和车辆信息
	if in.EbikeId != nil && !Ebike.IsValid(*in.EbikeId) {
		return errors.New("bike is not valid")
	}

	if in.EbikeId != nil {
		bikeInfo, _ := CacheBike.GetBikeInfoById(*in.EbikeId)
		if bikeInfo != nil && bikeInfo.SNCode != "" {
			trace_log.DebugLogger(bikeInfo.SNCode, "setting update req:", utils.ToJsonString(in))
		}
	}

	// todo:加事务
	if in.EbikeId != nil {
		dict["ebike_id"] = in.EbikeId
	}

	if in.HillAssist != nil {
		dict["hill_assist"] = in.HillAssist
	}

	// do other update here
	if in.HillAssist != nil {
		dict["hill_assist"] = in.HillAssist
	}

	if in.HillAssistRange != nil {
		dict["hill_assist_range"] = in.HillAssistRange
	}

	if in.FlyCar != nil {
		dict["fly_car"] = in.FlyCar
	}

	if in.Type != nil {
		dict["type"] = in.Type
	}

	// 先读取json数据
	var settings *entity.Setting
	if in.MsgSet != nil || in.EbikeSet != nil {
		var err error
		settings, err = a.iSetting.Find(&model.SettingUpdateRequest{
			Id:      in.Id,
			EbikeId: in.EbikeId,
		})
		if err != nil {
			return err
		}
	}

	if in.MsgSet != nil && settings != nil && settings.MsgSet != nil {
		if in.MsgSet.Shake != nil {
			settings.MsgSet.Shake = *(in.MsgSet.Shake)
		}
		if in.MsgSet.BlackOut != nil {
			settings.MsgSet.BlackOut = *(in.MsgSet.BlackOut)
		}
		if in.MsgSet.Hitchs != nil {
			settings.MsgSet.Hitchs = *(in.MsgSet.Hitchs)
		}
		if in.MsgSet.Handrail != nil {
			settings.MsgSet.Handrail = *(in.MsgSet.Handrail)
		}
		if in.MsgSet.StealAlarm != nil {
			settings.MsgSet.StealAlarm = *(in.MsgSet.StealAlarm)
		}
		if in.MsgSet.ChargeMonitor != nil {
			settings.MsgSet.ChargeMonitor = *(in.MsgSet.ChargeMonitor)
		}
		dict["MsgSet"] = settings.MsgSet
	}

	if in.EbikeSet != nil && settings != nil {
		if in.EbikeSet.PowerWarn != nil {
			settings.EbikeSet.PowerWarn = *(in.EbikeSet.PowerWarn)
		}
		if in.EbikeSet.PowerValue != nil {
			settings.EbikeSet.PowerValue = *(in.EbikeSet.PowerValue)
		}
		if in.EbikeSet.LeaveLock != nil {
			settings.EbikeSet.LeaveLock = *(in.EbikeSet.LeaveLock)
		}
		if in.EbikeSet.LeaveCountdown != nil {
			settings.EbikeSet.LeaveCountdown = *(in.EbikeSet.LeaveCountdown)
		}
		if in.EbikeSet.OpenLock != nil {
			settings.EbikeSet.OpenLock = *(in.EbikeSet.OpenLock)
		}
		if in.EbikeSet.OpenLockDistance != nil {
			settings.EbikeSet.OpenLockDistance = *(in.EbikeSet.OpenLockDistance)
		}
		if in.EbikeSet.HitchsUp != nil {
			settings.EbikeSet.HitchsUp = *(in.EbikeSet.HitchsUp)
		}
		if in.EbikeSet.Alarm != nil {
			settings.EbikeSet.Alarm = *(in.EbikeSet.Alarm)
		}
		if in.EbikeSet.VibrationSensitivity != nil {
			settings.EbikeSet.VibrationSensitivity = *(in.EbikeSet.VibrationSensitivity)
		}
		if in.EbikeSet.ElectronicFence != nil {
			settings.EbikeSet.ElectronicFence = *(in.EbikeSet.ElectronicFence)
		}
		if in.EbikeSet.FenceCenter != nil {
			settings.EbikeSet.FenceCenter = po.Point{
				X: in.EbikeSet.FenceCenter.X,
				Y: in.EbikeSet.FenceCenter.Y,
			}
		}
		if in.EbikeSet.FenceRadius != nil {
			settings.EbikeSet.FenceRadius = *(in.EbikeSet.FenceRadius)
		}
		dict["ebike_set"] = settings.EbikeSet
	}

	return a.iSetting.Update(in.Id, dict)
}

func (a *setting) Delete(ctx context.Context, in *model.SettingDeleteRequest) error {
	if in.EbikeId > 0 {
		info, _ := a.iSetting.Find(&model.SettingUpdateRequest{
			EbikeId: &in.EbikeId,
		})

		if info.Id == 0 {
			return fmt.Errorf("setting is not exist")
		}

		return a.iSetting.Delete(info.Id)
	}

	return a.iSetting.Delete(in.Id)
}

func (a *setting) List(ctx context.Context, in *model.SettingListRequest) (*model.SettingListResponse, error) {
	var (
		err   error
		total int
		list  []*entity.Setting
		out   = &model.SettingListResponse{}
	)

	// todo: 检查车辆是否有效
	if !Ebike.IsValid(in.EbikeId) {
		return nil, errors.EbikeNotExist.Error()
	}

	if total, list, err = a.iSetting.List(in); err != nil {
		return nil, err
	}

	// todo: 判断是否存在，不存在就创建设置， 临时这么设计
	if total == 0 {
		userId, _ := auth.ContextUserID(ctx)
		a.createSettings(&event.BindBike{
			BikeId: in.EbikeId,
			UserId: userId,
		})
		// 查询列表
		if total, list, err = a.iSetting.List(in); err != nil {
			return nil, err
		}
	}

	out.Total = total
	out.List = model.SettingsEntityToDto(list)
	return out, nil
}

// IsOverElectronicFence 是否超出电子围栏
func (a *setting) IsOverElectronicFence(sn string, bikeId int64) bool {
	settings, _ := postgres.Setting.Find(&model.SettingUpdateRequest{
		EbikeId: &bikeId,
	})

	bikeInfo, _ := CacheBike.GetBikeInfoById(settings.EbikeId)
	if bikeInfo.SNCode == "" {
		trace_log.DebugLogger(sn, "bikeInfo.SNCode is empty")
		return false
	}

	// 围栏大小是否有变更
	if settings.EbikeSet.ElectronicFence == 0 {
		trace_log.DebugLogger(sn, "settings.EbikeSet.ElectronicFence is 0")
		return false
	}
	// 获取当前车辆位置，和围栏中心点位置，计算是否在围栏内
	// 获取车辆位置
	rsp, _ := grpc.SCmd.GetShadowAttributeKeys(context.Background(), &dto.GetShadowAttributeKeysRequest{
		DeviceName: bikeInfo.SNCode,
		Key:        "location",
	})

	var point po.Point
	err := json.Unmarshal([]byte(rsp.Value), &point)
	if err != nil {
		trace_log.DebugLogger(sn, "json.Unmarshal error", err)
		return false
	}

	// 计算距离
	distance := infra.Lbs.GeoDistance(settings.EbikeSet.FenceCenter.X, settings.EbikeSet.FenceCenter.Y, point.X, point.Y, "K")

	// 默认50米
	if settings.EbikeSet.FenceRadius == 0 {
		settings.EbikeSet.FenceRadius = 10000
	}

	trace_log.DebugLogger(sn, "distance", distance, " FenceRadius:", settings.EbikeSet.FenceRadius, "point:", utils.ToJsonString(point))

	return int(distance) >= settings.EbikeSet.FenceRadius
}

func buildSetting(userId int64, in *model.SettingCreateRequest) *entity.Setting {
	// todo: check the entity is required
	return &entity.Setting{

		UserId: userId,

		EbikeId: in.EbikeId,

		EbikeSet: &po.EbikeSet{
			PowerWarn:            config.Conf.SystemSetting.EbikeSet.PowerWarn,
			PowerValue:           config.Conf.SystemSetting.EbikeSet.PowerValue,
			LeaveLock:            config.Conf.SystemSetting.EbikeSet.LeaveLock,
			LeaveCountdown:       config.Conf.SystemSetting.EbikeSet.LeaveCountdown,
			OpenLock:             config.Conf.SystemSetting.EbikeSet.OpenLock,
			OpenLockDistance:     config.Conf.SystemSetting.EbikeSet.OpenLockDistance,
			HitchsUp:             config.Conf.SystemSetting.EbikeSet.HitchsUp,
			Alarm:                config.Conf.SystemSetting.EbikeSet.Alarm,
			VibrationSensitivity: config.Conf.SystemSetting.EbikeSet.VibrationSensitivity,
			ElectronicFence:      config.Conf.SystemSetting.EbikeSet.ElectronicFence,
			FenceCenter: po.Point{
				config.Conf.SystemSetting.EbikeSet.FenceCenter.X,
				config.Conf.SystemSetting.EbikeSet.FenceCenter.Y,
			},
			FenceRadius: config.Conf.SystemSetting.EbikeSet.FenceRadius,
		},

		HillAssist: config.Conf.SystemSetting.HillAssist,

		HillAssistRange: config.Conf.SystemSetting.HillAssistRange,

		FlyCar: config.Conf.SystemSetting.FlyCar,

		MsgSet: &po.MsgSet{
			StealAlarm:    config.Conf.SystemSetting.MsgSet.StealAlarm,
			Hitchs:        config.Conf.SystemSetting.MsgSet.Hitchs,
			Shake:         config.Conf.SystemSetting.MsgSet.Shake,
			BlackOut:      config.Conf.SystemSetting.MsgSet.BlackOut,
			ChargeMonitor: config.Conf.SystemSetting.MsgSet.ChargeMonitor,
			Handrail:      config.Conf.SystemSetting.MsgSet.Handrail,
		},

		Type: config.Conf.SystemSetting.Type,

		CreatedAt: time.Now().Unix(),

		UpdatedAt: time.Now().Unix(),
	}
}
